/**
 * 
 */
package org.ronsmits.config.copier;

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.Properties;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * @author ron
 * 
 */
public class Command {
    private final static Logger logger = LoggerFactory.getLogger(Command.class);
    private final static String APPLICATIONNAME = "config.applicationname";
    private final static String ENVIRONMENT = "config.environment";
    private final static String CONFIGHOME = "config.confighome";
    private final static String SERVER = "config.configserver";
    private String server;
    private String applicationname;
    private String environment;
    private String confighome;

    Command() {
        if (readinProperties())
            try {
                Properties properties = getProperties(applicationname, environment);
                for (String key : properties.stringPropertyNames()) {
                    String value = properties.getProperty(key);
                    process(applicationname, environment, confighome, key, value);
                    logger.debug("asking for {} to put in {}", key, value);
                }
            } catch (Exception e) {
                logger.error("an error occurred during processing!", e);
                System.exit(-1); // signal the error to the caller
            }
        System.exit(0);
    }

    /**
     * process each entry
     * 
     * @param app
     * @param env
     * @param confighome
     * @param sourceFile
     * @param targetFile
     * @throws Exception
     */
    private void process(String app, String env, String confighome, String sourceFile, String targetFile) throws Exception {
        logger.info("processing {} to {}", sourceFile, targetFile);
        String buf = readFromServer(app, env, sourceFile);
        save(buf, confighome, targetFile);

    }

    /**
     * save the buffer content into outFilename. The method will create any
     * directories needed
     * 
     * @param buf
     * @param home
     * @param targetFilename
     * @throws Exception
     */
    private void save(String buf, String home, String targetFilename) throws Exception {
        File out = new File(home + File.separator + targetFilename);
        String dirs = out.getAbsolutePath().substring(0, out.getAbsolutePath().indexOf(out.getName()));
        File dir = new File(dirs);
        if (!dir.exists() && !dir.mkdirs()) {
            logger.error("not able to create {}", dirs);
            throw new Exception("could not create " + dirs);
        }
        BufferedWriter bout = new BufferedWriter(new FileWriter(out));
        bout.write(buf);
        bout.close();
    }

    /**
     * Read the sourceFile into String.
     * 
     * @param app
     * @param env
     * @param sourceFile
     * @return
     * @throws IOException
     */
    private String readFromServer(String app, String env, String sourceFile) throws IOException {
        URL url = makeUrl(app, env, sourceFile);
        StringBuffer buffer = new StringBuffer();
        BufferedReader reader = new BufferedReader(new InputStreamReader(url.openStream()));
        char[] buf = new char[2048];

        int numRead = 0;
        while ((numRead = reader.read(buf)) != -1) {
            String readData = String.valueOf(buf, 0, numRead);
            buffer.append(readData);
        }
        return buffer.toString();
    }

    /**
     * Utility method to make an URL
     * 
     * @param app
     * @param env
     * @param key
     * @return
     * @throws MalformedURLException
     */
    private URL makeUrl(String app, String env, String key) throws MalformedURLException {
        URL url = new URL(server + "/" + app + "/" + env + "/" + key);
        return url;
    }

    /**
     * Ask the configserver for the command file. This file contains the names
     * of all the files that need be transferred and the correct place and name
     * where they need to be placed in this system
     * 
     * @param appname
     * @param env
     * @return
     * @throws IOException
     */
    private Properties getProperties(String appname, String env) throws IOException {
        Properties prop = new Properties();
        URL url = makeUrl(appname, env, "command");
        logger.debug("asking configserver for {}", url);
        prop.load(url.openStream());
        return prop;
    }

    /**
     * <p>
     * Startup class for the configcopier. It expects to find 4 system
     * properties:
     * <ul>
     * <li>config.application (like "testapplication")
     * <li>config.enviromment (like "test")
     * <li>config.confighome (like "/opt/configuration") this directory must exist
     * and will contain the files it receives from the configuration server
     * <li>config.configserver (the url into the configurationserver)
     * </ul>
     * To set these from the command line, call the program as
     * 
     * <code>java -Dgd.application=raptools -Dgd.environment=test33 -Dgd.confighome=/opt/configuration</code>
     * 
     * @param args
     */
    public static void main(String[] args) {
        logger.debug("started configcopier");
        new Command();
    }

    /**
     * Read in the properties from the environment.
     * 
     * @return true if all went well, otherwise false
     */
    private boolean readinProperties() {
        boolean ok = false;
        applicationname = System.getProperty(APPLICATIONNAME);
        environment = System.getProperty(ENVIRONMENT);
        confighome = System.getProperty(CONFIGHOME);
        server = System.getProperty(SERVER);

        if (applicationname == null || environment == null || confighome == null || server == null) {
            logger.error("one or more of the system parameters is not set\n\tapplicationname=" + applicationname + "\n\tenvironment="
                    + environment + "\n\tconfighome=" + confighome + "\n\tserver=" + server);
            return false;
        } else
            ok = true;
        return ok;
    }

}
